/*
 * SplitPane.java  0.4.0 / Oct 26, 2010
 *
 * Copyright (c) 2010 - 2011, cute4j.com and contributors.
 *
 * This software is licensed under the LGPL version 2.1 license.
 * A copy of this license is included in the file LICENSE-LGPL.txt.
 */
package com.cute4j.swing;

import com.cute4j.swing.properties.SplitPaneProperties;

/**
 * Split Pane Component.
 *
 * @cute4j.native
 *    generate="header"
 *
 * @author Slobodan Vrkacevic
 * @version 0.4.0
 */
public class SplitPane extends Component
                       implements SwingConstants, SplitPaneProperties {

// Package-Private Static Fields. ----------------------------------------------

  static final int DEFAULT_ORIENTATION = HORIZONTAL;

// Static Initialization. ------------------------------------------------------

  static {
    _initialize();
  }

// Private Fields. -------------------------------------------------------------

  private int orientation;
  private boolean continuousLayout;
  private double resizeWeight;
  private boolean gatherDividerSize;
  private int dividerSize;
  private int dividerLocation;
  private boolean oneTouchExpandable;
  private Component leftComponent;
  private Component rightComponent;

// Public Constructors. --------------------------------------------------------

  /**
   * Constructs new SplitPane object.
   */
  public SplitPane() {
    this(DEFAULT_ORIENTATION, true, null, null);
  }

  /**
   * Constructs new SplitPane object.
   *
   * @param orientation the orientation
   */
  public SplitPane(int orientation) {
    this(orientation, true, null, null);
  }

  /**
   * Constructs new SplitPane object.
   *
   * @param orientation the orientation
   * @param continuousLayout <tt>true</tt> if resize of split pane's components
   *                         is continuous
   */
  public SplitPane(int orientation,  boolean continuousLayout) {
    this(orientation, continuousLayout, null, null);
  }

  /**
   * Constructs new SplitPane object.
   *
   * @param orientation the orientation
   * @param leftComponent the left component
   * @param rightComponent the right component
   */
  public SplitPane(int orientation, Component leftComponent,
                     Component rightComponent) {
    this(orientation, true, leftComponent, rightComponent);
  }

  /**
   * Constructs new SplitPane object.
   *
   * @param leftComponent the left component
   * @param rightComponent the right component
   */
  public SplitPane(Component leftComponent, Component rightComponent) {
    this(DEFAULT_ORIENTATION, true, leftComponent, rightComponent);
  }

  /**
   * Constructs new SplitPane object.
   *
   * @param orientation the orientation
   * @param continuousLayout <tt>true</tt> if resize of split pane's components
   *                         is continuous
   * @param leftComponent the left component
   * @param rightComponent the right component
   */
  public SplitPane(int orientation,  boolean continuousLayout,
                     Component leftComponent, Component rightComponent) {
    if ((orientation < HORIZONTAL) || (orientation > VERTICAL)) {
      throw new IllegalArgumentException("Invalid orientation");
    }
    if (orientation != DEFAULT_ORIENTATION) {
      _setOrientation(_delegate, VERTICAL); // NOPMD
    }
    this.orientation = orientation;
    gatherDividerSize = true;
    this.continuousLayout = continuousLayout;
    if (leftComponent != null) {
      if (leftComponent.isDisposed()) {
        throw new IllegalArgumentException("Left component disposed");
      }
      acquireComponent(leftComponent);
      _setLeftComponent(_delegate, leftComponent._delegate); // NOPMD
    }
    if (rightComponent != null) {
      if (rightComponent.isDisposed()) {
        throw new IllegalArgumentException("Right component disposed");
      }
      acquireComponent(leftComponent);
      _setRightComponent(_delegate, rightComponent._delegate); // NOPMD
    }
  }

// Public Methods. -------------------------------------------------------------

  /**
   * Returns orientation of the split pane.
   *
   * @return orientation of the split pane
   */
  public int getOrientation() {
    return orientation;
  }

  /**
   * Sets orientation.
   *
   * @param orientation the orientation
   */
  public void setOrientation(int orientation) {
    final int oldOrientation = this.orientation;
    if (oldOrientation != orientation) {
      if ((orientation < HORIZONTAL) || (orientation > VERTICAL)) {
        throw new IllegalArgumentException("Invalid orientation");
      }
      disposedCheck();
      _setOrientation(_delegate, orientation);
      this.orientation = orientation;
      firePropertyChange(PROPERTY_ORIENTATION, oldOrientation, orientation);
    }
  }

  /**
   * Returns <tt>true</tt> if the resize of split pane's components is
   * continuous.
   *
   * @return <tt>true</tt> if the resize of split pane's components is
   *         continuous
   */
  public boolean isContinuousLayout() {
    return continuousLayout;
  }

  /**
   * Sets whether or not the resize of split pane's components is continuous.
   *
   * @param continuousLayout <tt>true</tt> if continuous
   */
  public void setContinuousLayout(boolean continuousLayout) {
    final boolean oldContinuousLayout = this.continuousLayout;
    if (oldContinuousLayout != continuousLayout) {
      disposedCheck();
      _setContinuousLayout(_delegate, continuousLayout);
      this.continuousLayout = continuousLayout;
      firePropertyChange(PROPERTY_CONTINUOUS_LAYOUT, oldContinuousLayout,
                         continuousLayout);
    }
  }

  /**
   * Returns the resize weight.
   *
   * @return the resize weight
   */
  public double getResizeWeight() {
    return resizeWeight;
  }

  /**
   * Sets resize weight.
   *
   * @param resizeWeight the resize weight
   */
  public void setResizeWeight(double resizeWeight) {
    if ((resizeWeight < 0) || (resizeWeight > 1)) {
      throw new IllegalArgumentException(
                                      "Resize weight must be between 0 and 1");
    }
    final double oldResizeWeight = this.resizeWeight;
    if (Double.doubleToRawLongBits(oldResizeWeight) !=
        Double.doubleToRawLongBits(resizeWeight)) {
      disposedCheck();
      _setResizeWeight(_delegate, resizeWeight);
      this.resizeWeight = resizeWeight;
      firePropertyChange(PROPERTY_RESIZE_WEIGHT, oldResizeWeight, resizeWeight);
    }
  }

  /**
   * Returns divider size.
   *
   * @return divider size
   */
  public int getDividerSize() {
    if (gatherDividerSize) {
      dividerSize = _getDividerSize(_delegate);
      gatherDividerSize = false;
    }
    return dividerSize;
  }

  /**
   * Sets divider size.
   *
   * @param dividerSize the divider size
   */
  public void setDividerSize(int dividerSize) {
    if (dividerSize <= 0) {
      throw new IllegalArgumentException("Invalid divider size");
    }
    final int oldDividerSize = this.dividerSize;
    if (oldDividerSize != dividerSize) {
      disposedCheck();
      _setDividerSize(_delegate, dividerSize);
      this.dividerSize = dividerSize;
      firePropertyChange(PROPERTY_DIVIDER_SIZE, oldDividerSize, dividerSize);
    }
  }

  /**
   * Returns divider location.
   *
   * @return divider location
   */
  public int getDividerLocation() {
    return dividerLocation;
  }

  /**
   * Sets divider location.
   *
   * @param dividerLocation the divider location
   */
  public void setDividerLocation(int dividerLocation) {
    disposedCheck();
    _setDividerLocation(_delegate, dividerLocation);
  }

  /**
   * Returns <tt>true</tt> if the components of the split pane can be quickly
   * expanded/collapsed.
   *
   * @return <tt>true</tt> if the components of the split pane can be quickly
   * expanded/collapsed.
   */
  public boolean isOneTouchExpandable() {
    return oneTouchExpandable;
  }

  /**
   * Sets whether or not the components of the split pane can be quickly
   * expanded/collapsed.
   *
   * @param oneTouchExpandable <tt>true</tt> if expandable
   */
  public void setOneTouchExpandable(boolean oneTouchExpandable) {
    final boolean oldOneTouchExpandable = this.oneTouchExpandable;
    if (oldOneTouchExpandable != oneTouchExpandable) {
      disposedCheck();
      _setOneTouchExpandable(_delegate, oneTouchExpandable);
      this.oneTouchExpandable = oneTouchExpandable;
      firePropertyChange(PROPERTY_ONE_TOUCH_EXPANDABLE, oldOneTouchExpandable,
                         oneTouchExpandable);
    }
  }

  /**
   * Returns left component.
   *
   * @return left component
   */
  public Component getLeftComponent() {
    return leftComponent;
  }

  /**
   * Sets left component.
   *
   * @param leftComponent the left component
   *
   * @return old left component
   */
  public Component setLeftComponent(Component leftComponent) {
    Component oldLeftComponent = this.leftComponent;
    if (oldLeftComponent != leftComponent) { // NOPMD
      disposedCheck();
      releaseComponent(oldLeftComponent);
      if (leftComponent != null) {
        if (leftComponent.isDisposed()) { // NOPMD
          throw new IllegalArgumentException("Left component disposed");
        }
        acquireComponent(leftComponent);
        _setLeftComponent(_delegate, leftComponent._delegate);
      }
      this.leftComponent = leftComponent;
    }
    return oldLeftComponent;
  }

  /**
   * Returns right component.
   *
   * @return right component
   */
  public Component getRightComponent() {
    return rightComponent;
  }

  /**
   * Sets right component.
   *
   * @param rightComponent the right component
   */
  public void setRightComponent(Component rightComponent) {
    Component oldRightComponent = this.rightComponent;
    if (oldRightComponent != rightComponent) { // NOPMD
      disposedCheck();
      releaseComponent(oldRightComponent);
      if (rightComponent != null) {
        if (rightComponent.isDisposed()) { // NOPMD
          throw new IllegalArgumentException("Right component disposed");
        }
        acquireComponent(rightComponent);
        _setRightComponent(_delegate, rightComponent._delegate);
      }
      this.rightComponent = rightComponent;
    }
  }

  /**
   * Returns top component.
   *
   * @return top component
   */
  public Component getTopComponent() {
    return leftComponent;
  }

  /**
   * Sets top component.
   *
   * @param topComponent top component
   */
  public void setTopComponent(Component topComponent) {
    setLeftComponent(topComponent);
  }

  /**
   * Returns bottom component.
   *
   * @return bottom component
   */
  public Component getBottomComponent() {
    return rightComponent;
  }

  /**
   * Sets bottom component.
   *
   * @param bottomComponent bottom component
   */
  public void setBottomComponent(Component bottomComponent) {
    setRightComponent(bottomComponent);
  }

// Overridden Methods. ---------------------------------------------------------

  /**
   * {@inheritDoc}
   */
  public Component[] getOwnedComponents() {
    if ((leftComponent != null) && (rightComponent != null)) { // NOPMD
      return new Component[] {leftComponent, rightComponent};
    } else  if (leftComponent != null) { // NOPMD
      return new Component[] {leftComponent};
    } else  if (rightComponent != null) {
      return new Component[] {rightComponent};
    }
    return EMPTY_COMPONENT_ARRAY; // NOFB NOPMD
  }

  void releaseComponent(Component component) {
    if (component != null) {
      if (leftComponent == component) { // NOPMD
        _setLeftComponent(_delegate, 0);
        leftComponent = null;
        component.owner = null;
      } else if (rightComponent == component) { // NOPMD
        _setRightComponent(_delegate, 0);
        rightComponent = null;
        component.owner = null;
      }
    }
  }

  /*!
   * @cute4j.native
   *    overridden="true"
   */
  void _styleChanged() {
    super._styleChanged();
    gatherDividerSize = true;
  }

// Native Callbacks. -----------------------------------------------------------

  void _dividerLocationChanged(int dividerLocation) {
    final int oldDividerLocation = this.dividerLocation;
    if (oldDividerLocation != dividerLocation) {
      this.dividerLocation = dividerLocation;
      firePropertyChange(PROPERTY_DIVIDER_LOCATION, oldDividerLocation,
                         dividerLocation);
    }
  }

// Package-Private Native Methods. ---------------------------------------------

  native long _create();

  native void _setLeftComponent(long delegate, long componentdelegate);

  native void _setRightComponent(long delegate, long componentdelegate);

  native void _setOrientation(long delegate, int orientation);

  native void _setContinuousLayout(long delegate, boolean continuousLayout);

  native void _setResizeWeight(long delegate, double resizeWeight);

  native int _getDividerSize(long delegate);

  native void _setDividerSize(long delegate, int dividerSize);

  native void _setDividerLocation(long delegate, int dividerLocation);

  native void _setOneTouchExpandable(long delegate, boolean oneTouchExpandable);

// Private Static Native Methods. ----------------------------------------------

  private static native void _initialize();

}
